package cn.wizzer.app.user.modules.service.impl;

import cn.wizzer.app.sys.modules.models.*;
import cn.wizzer.app.sys.modules.services.*;
import cn.wizzer.app.user.modules.models.User;
import cn.wizzer.app.user.modules.models.UserRecharge;
import cn.wizzer.app.user.modules.models.constant.GlobalPayParamConstant;
import cn.wizzer.app.user.modules.models.constant.PayNotifyConstant;
import cn.wizzer.app.user.modules.models.dto.ElephantRechargeDTO;
import cn.wizzer.app.user.modules.models.dto.GlobalPayRechargeDTO;
import cn.wizzer.app.user.modules.models.dto.XindovndRechargeDTO;
import cn.wizzer.app.user.modules.models.enums.BizTypeEnum;
import cn.wizzer.app.user.modules.models.enums.PayChannel;
import cn.wizzer.app.user.modules.models.enums.RechargeStatusEnum;
import cn.wizzer.app.user.modules.models.enums.UserColEnum;
import cn.wizzer.app.user.modules.models.vo.UserRechargeVO;
import cn.wizzer.app.user.modules.services.UserService;
import cn.wizzer.app.user.modules.services.account.UserAccountRecordService;
import cn.wizzer.app.user.modules.services.account.UserRechargeService;
import cn.wizzer.app.user.util.ElephantUtil;
import cn.wizzer.app.user.util.GlobalPayUtil;
import cn.wizzer.app.user.util.OneWalletUtil;
import cn.wizzer.app.user.util.XindovndUtil;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.*;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONObject;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Random;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = UserRechargeService.class)
public class UserRechargeServiceImpl extends BaseServiceImpl<UserRecharge> implements UserRechargeService {

    private static final Log log = Logs.get();

    public UserRechargeServiceImpl(Dao dao) {
        super(dao);
    }

    @Inject
    private UserAccountRecordService recordService;

    @Inject
    private UserService userService;

    @Inject
    private CommonDBService commonDBService;

    @Inject
    @Reference(retries = 3, timeout = 10000)
    private SysMsgService userMsgService;

    @Inject
    @Reference
    private SysCollectCardService sysCollectCardService;

    @Inject
    @Reference
    private SysParamService paramService;

    @Inject
    @Reference
    private SysCurrencyService sysCurrencyService;

    @Inject
    @Reference
    private SysErcAddressService sysErcAddressService;

    @Inject
    private RedisService redisService;

    @Override
    public UserRecharge add(int amount, String appid, PayChannel channel, String currencyId, String bankLibId) {
        log.info("user recharge amount:" + amount + ",appid:" + appid + ",channel:" + channel + ",currencyId:" + currencyId + ",bankLibId:" + bankLibId);
        if (amount <= 0) {
            Result.breakWith(BizExceptionEnum.ENSURE_BEHAVIORAL_SAFETY);
        }
        UserRecharge recharge = new UserRecharge();
        //待确认
        recharge.setStatus(RechargeStatusEnum.PAYING);
        String sign = String.valueOf(new Random().nextInt(9000) + 1000);
        recharge.setSign(sign);
        BigDecimal decimalAmount = new BigDecimal(amount);
        recharge.setAmount(decimalAmount);

        SysCurrency currency = sysCurrencyService.getById(currencyId);

        recharge.setCurrencyId(currency.getId());

        if (PayChannel.BANK.equals(channel)) {
            //region 选择银行卡
            if (!currency.getRechargeEnable()) {
                Result.breakWith(BizExceptionEnum.CURRENCY_NOT_ALLOW_RECHARGE);
            }
            if (currency.getDigital()) {
                Result.breakWith(BizExceptionEnum.DIGITAL_CURRENCY_NOW_ALLOW_RECHARGE_BY_BANK);
            }
            //根据币种查询支持的收款卡
            SysCollectCard card = sysCollectCardService.getRandomCard(bankLibId);
            if (card == null) {
                Result.breakWith(BizExceptionEnum.COLLECT_CARD_NOT_EXIST);
            }
            recharge.setAddress(card.getId());
            recharge.setCardNo(card.getNo());
            //endregion
        } else if (PayChannel.ERC20.equals(channel)) {
            if (!currency.getDigital()) {
                Result.breakWith(BizExceptionEnum.LEGAL_CURRENCY_NOW_ALLOW_RECHARGE_BY_DIGITAL_CHANNEL);
            }
            SysErcAddress sysErcAddress = sysErcAddressService.randomAddress();
            if (sysErcAddress == null) {
                Result.breakWith(BizExceptionEnum.ERC20_ADDRESS_NOT_SET);
            }
            String ercAddress = sysErcAddress.getAddress();
            recharge.setAddress(ercAddress);
            recharge.setCardNo(ercAddress);
            //半小时内是否存在同样金额订单
            UserRecharge fetch = this.fetch(Cnd.where("ctAt", ">=", System.currentTimeMillis() / 1000 - (60 * 30))
                    .and("status", "in", Result.createList(RechargeStatusEnum.PAYING, RechargeStatusEnum.PAID))
                    .and("amount", "=", decimalAmount));
            if (fetch != null) {
                Result.breakWith(BizExceptionEnum.RETRY);
            }
        }
        recharge.setUserId(appid);
        recharge.setCtAt(System.currentTimeMillis());
        recharge.setNo(Id.createOrderNo());
        recharge.setChannel(channel);
        this.insert(recharge);


        log.info("acd recharge result:" + recharge);
        return recharge;
    }

    @Override
    public void manual(BigDecimal amount, String toUser, String updaterId, String currencyId, PayChannel channel) {
        UserRecharge recharge = new UserRecharge();
        recharge.setAmount(amount);
        //手动
        recharge.setSign("0000");
        recharge.setCurrencyId(currencyId);
        recharge.setNo(Id.createOrderNo());
        recharge.setUserId(toUser);
        recharge.setChannel(channel);
        recharge.setAddress("PLATFORM");
        recharge.setPayer("PLATFORM");
        recharge.setNo(Id.createOrderNo());
        recharge.setPaymentAt(Times.getTS());
        recharge.setStatus(RechargeStatusEnum.PAID);

        this.insert(recharge);
    }

    @Override
    public Pagination listPage(String conditionData, String orderNo, String searchKeyword, String[] searchDate, RechargeStatusEnum status,
                               PayChannel channel, String currencyId, int times, int pageNumber, int pageSize) {
        Sql sql = Sqls.create("SELECT r.*, u.userName, u.`nickName`, u.`phone`,sc.name currencyName " +
                "FROM user_recharge r ");
        Sql countSql = Sqls.create("SELECT COUNT(1) FROM user_recharge r ");
        Sql joinSql = Sqls.create("LEFT JOIN sys_currency sc ON sc.id = r.currencyId " +
                "LEFT JOIN USER u ON u.id = r.userId " +
                "$condition ");
        Cnd cnd = Cnd.where("r.delFlag", "=", 0);
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    cnd.and("u." + colName, "=", 0);
                }
            }
        }
        if (Strings.isNotBlank(orderNo)) {
            cnd.and("r.no", "=", orderNo);
        }
        if (Strings.isNotBlank(searchKeyword)) {
            cnd.and(Cnd.exps("u.userName", "=", searchKeyword)
                    .or("u.phone", "like", "%" + searchKeyword + "%"));
        }
        if (searchDate != null && searchDate.length > 0) {
            cnd.and("r.ctAt", ">=", Times.d2TS(Times.D(searchDate[0])));
            cnd.and("r.ctAt", "<=", Times.d2TS(Times.D(searchDate[1])));
        }
        if (status != null) {
            cnd.and("r.status", "=", status);
        }
        if (channel != null) {
            cnd.and("r.channel", "=", channel);
        }
        if (Strings.isNotBlank(currencyId)) {
            cnd.and("r.currencyId", "=", currencyId);
        }
        if (times != -1) {
            cnd.and("r.times", "=", times);
        }
        joinSql.setCondition(cnd);
        sql.appendSourceSql(joinSql.toString());
        sql.appendSourceSql("ORDER BY r.ctAt DESC");
        countSql.appendSourceSql(joinSql.toString());
        return commonDBService.listPage(UserRechargeVO.class, pageNumber, pageSize, sql.toString(), countSql.toString());
    }

    @Override
    public BigDecimal searchSum(String conditionData, String orderNo, String searchKeyword, String[] searchDate, RechargeStatusEnum status, PayChannel channel, String currencyId, int times) {
        StringBuffer sb = new StringBuffer();
        sb.append("select IFNULL(sum(r.amount * c.rate), 0) from user_recharge r left join sys_currency c on r.currencyId=c.id ");
        sb.append(" left join user u on u.id = r.userId ");
        sb.append(" where 1=1");

        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    sb.append(" and u.").append(colName).append(" = 0");
                }
            }
        }
        if (Strings.isNotBlank(orderNo)) {
            sb.append(" and r.no = '").append(orderNo).append("'");
        }
        if (Strings.isNotBlank(searchKeyword)) {
            sb.append(" and ( u.userName = '").append(searchKeyword).append("'");
            sb.append(" or u.phone like '%").append(searchKeyword).append("%' )");
        }
        if (searchDate != null && searchDate.length > 0) {
            sb.append(" and r.ctAt >=").append(Times.d2TS(Times.D(searchDate[0])));
            sb.append(" and r.ctAt <=").append(Times.d2TS(Times.D(searchDate[1])));
        }
        if (status != null) {
            sb.append(" and r.status = '").append(status).append("'");
        }
        if (channel != null) {
            sb.append(" and r.channel = '").append(channel).append("'");
        }
        if (Strings.isNotBlank(currencyId)) {
            sb.append(" and r.currencyId = '").append(currencyId).append("'");
        }
        if (times != -1) {
            sb.append(" and r.times = '").append(times).append("'");
        }

        Sql sql = Sqls.create(sb.toString());
        sql.setCallback(Sqls.callback.str());
        this.dao().execute(sql);
        return new BigDecimal(sql.getString()).setScale(6, BigDecimal.ROUND_DOWN);
    }

    @Aop(TransAop.REPEATABLE_READ)
    @Override
    public UserRecharge examine(String id, Boolean sure, String remark, String rAmount, String opBy) {
        UserRecharge record = this.fetch(id);
        if (remark != null) {
            record.setRemark(remark);
        }
        if (record.getStatus() != RechargeStatusEnum.PAID) {
            Result.breakWith(BizExceptionEnum.THE_ORDER_HAS_BEEN_OPERATED);
        }
        record.setOpBy(opBy);
        record.setOpAt(Times.getTS());
        record.setExamineAt(Times.getTS());
        String title;
        String result;
        if (sure) {
            //已完成
            record.setStatus(RechargeStatusEnum.SUCCESS);
            if ("MANUAL".equals(rAmount)) {//如果是后台人工，则直接取充值记录的金额，这个原来的接口有问题，特殊处理一下，传个MANUAL作为识别标志
                recordService.add(record.getCurrencyId(), record.getUserId(), record.getAmount(), BizTypeEnum.RECHARGE, id, record.getNo());
            } else {
                recordService.add(record.getCurrencyId(), record.getUserId(), new BigDecimal(rAmount), BizTypeEnum.RECHARGE, id, record.getNo());
            }
            // 如果是非后端直接充值，则次数加1
            if (!PayChannel.PLAT_FORM.equals(record.getChannel())) {
                // 查询出当前用户非后端充值的成功记录
                int num = this.count(Cnd.where("userId", "=", record.getUserId()).and("status", "=", RechargeStatusEnum.SUCCESS)
                        .and("channel", "!=", PayChannel.PLAT_FORM));
                // 充值成功 累计充值成功次数+1
                record.setTimes(num + 1);

                // 更新下充值情况
                redisService.set("userId:recharged:" + record.getId(), "yes");
            }

            // 更新用户提现流水限制额度
            // 获取用户倍数 multi
            // 根据充值币种算出USD
            // amount*multi
            User user = userService.fetch(record.getUserId());
            SysCurrency sysCurrency = sysCurrencyService.getById(record.getCurrencyId());
            BigDecimal restriction = user.getMultiple().multiply(sysCurrency.getRate()).multiply(record.getAmount());
//            user.setTransRestriction(user.getTransRestriction().add(restriction.setScale(6, BigDecimal.ROUND_DOWN)));
            // 当有新充值，判断有提现额度限制是不是比有效流水小，如果小，则把有效流水作为充值后的基数
            BigDecimal baseAmount = user.getTransRestriction();
            if (baseAmount.compareTo(user.getEffectQuota()) == -1) {
                baseAmount = user.getEffectQuota();
            }
            user.setTransRestriction(baseAmount.add(restriction.setScale(6, BigDecimal.ROUND_DOWN)));
            userService.update(user);

            // "充值到账提醒"
            title = "Recharge to account reminder";
            // "成功"
            result = "successful";
        } else {
            //失败
            record.setStatus(RechargeStatusEnum.FAIL);
            // "充值失败通知"
            title = "Failed recharge notification";
            // "失败"
            result = "failed";
        }
        //构建消息通知
        SysMsg userMsg = userMsgService.createMsg("member", title, String.format("<p>Recharge " + result + "， Order Number<span style='color:red'>[%s]</span>，" +
                        "Recharge amount<span style='color:red'>[%s]</span>",
                record.getNo(), record.getAmount()), opBy);
        //发送消息给用户
        userMsgService.saveMsg(userMsg, new String[]{record.getUserId()});
        this.update(record);
        return record;
    }

    @Override
    public UserRechargeVO detail(String where) {
        StringBuffer sb = new StringBuffer();
        sb.append("SELECT r.*,lc.name currencyName,b.name as bankName,c.accountName,u.`nickName`,u.`email`")
                .append("FROM user_recharge r ")
                .append("LEFT JOIN USER u ON u.id = r.userId ")
                .append("LEFT JOIN sys_collect_card c ON c.id = r.address ")
                .append("LEFT JOIN sys_bank_lib b ON b.id = c.bankLibId ")
                .append("LEFT JOIN sys_currency lc ON lc.id = r.currencyId ")
                .append(where)
                .append(" order by r.opAt desc");
        UserRechargeVO userRechargeVo = commonDBService.fetchSql(UserRechargeVO.class, sb.toString());
        List<UserRechargeVO.RechargeStep> list = Result.createList(
                UserRechargeVO.createRechargeStep().setStatus(RechargeStatusEnum.PAYING).setAt(userRechargeVo.getCtAt()),
                UserRechargeVO.createRechargeStep().setStatus(RechargeStatusEnum.PAID).setAt(userRechargeVo.getPaymentAt()),
                userRechargeVo.getStatus().equals(RechargeStatusEnum.FAIL) ?
                        UserRechargeVO.createRechargeStep().setStatus(RechargeStatusEnum.FAIL).setAt(userRechargeVo.getExamineAt())
                        : UserRechargeVO.createRechargeStep().setStatus(RechargeStatusEnum.SUCCESS).setAt(userRechargeVo.getExamineAt())
        );
        //已取消的单
        if (userRechargeVo.getCancelAt() != null) {
            for (int i = 0; i < list.size(); i++) {
                //按流程某条内容为空时则插前面
                if (list.get(i + 1) != null && (list.get(i + 1).getAt() == null)) {
                    list.add(i + 1, UserRechargeVO.createRechargeStep().setAt(userRechargeVo.getCancelAt()).setStatus(RechargeStatusEnum.CANCELLED));
                    list = list.subList(0, i + 2);
                    break;
                }
            }
        }
        userRechargeVo.setSteps(list);
        // 不展示remark
        userRechargeVo.setRemark("");
        return userRechargeVo;
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public String oneWalletNotify(String respBody) throws Exception {
        SysParam sysParam = paramService.fetch();
        try {
            String decrypt = OneWalletUtil.decrypt(respBody, sysParam);
            JSONObject jsonObject = JSONUtil.parseToJSON(decrypt);
            BigDecimal paidAmount = jsonObject.getBigDecimal("paid_amount");
            String no = jsonObject.getString("sh_order_no");
            Integer orderStatus = jsonObject.getInteger("order_status");
            UserRecharge fetch = this.fetch(Cnd.where("no", "=", no));
            if (fetch.getAmount().compareTo(paidAmount) == 0 && orderStatus.equals(3)) {
                this.examine(fetch.getId(), true, null, paidAmount.toString(), "OneWalletNotify");
                this.update(Chain.make("paymentAt",
                        Times.getTS()),
                        Cnd.where("id", "=", fetch.getId()));
            } else {
                this.examine(fetch.getId(), false, null, paidAmount.toString(), "OneWalletNotify");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return OneWalletUtil.success(sysParam.getOneWalletSafeCode());
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public String oneWalletToPay(String appid, String currencyId, int amount, SysPayWayBank bank) throws Exception {
        SysParam sysParam = paramService.fetch();
        UserRecharge userRecharge = add(amount, appid, PayChannel.ONE_WALLET, currencyId, bank.getBankLibId());
        String ivKey = OneWalletUtil.randomByte();
        //商户安全码
        String cKey = sysParam.getOneWalletSafeCode();
        String reqBody = OneWalletUtil.parseRechargeParam(bank.getCode(), userRecharge.getId(), userRecharge.getAmount(), ivKey, cKey,
                sysParam.getOneWalletRechargeNotifyUrl());
        String respBody = OneWalletUtil.send(sysParam.getOneWalletToPayUrl(), reqBody);
        respBody = OneWalletUtil.decrypt(respBody, sysParam);
        JSONObject jsonObject = JSONUtil.parseToJSON(respBody).getJSONObject("data");
        String url = jsonObject.getJSONArray("url").getString(0);
        this.update(Chain.make("outNo", jsonObject.getString("order_id"))
                        .add("address", url)
                        .add("paymentAt", Times.getTS())
                        .add("status", RechargeStatusEnum.PAID),
                Cnd.where("id", "=", userRecharge.getId()));
        return url;
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public UserRecharge xindovndNotify(XindovndRechargeDTO xin) throws ParseException, UnsupportedEncodingException {
        log.info("xindovndNotify recharge " + xin);
        SysParam sysParam = paramService.fetch();
        if (XindovndUtil.verifyRechargeNotify(xin, sysParam.getXindovndSecret())) {
            UserRecharge fetch = this.fetch(Cnd.where("no", "=", xin.getOuttradeno()));
            if (fetch.getAmount().compareTo(new BigDecimal(xin.getAmount())) == 0 && xin.getStatus().equals("1")) {
                fetch = this.examine(fetch.getId(), true, null, xin.getRamount(), "XindovndNotify");
                this.update(Chain.make("paymentAt",
                        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(xin.getEndtime()).getTime() / 1000L),
                        Cnd.where("id", "=", fetch.getId()));
            } else if (xin.getStatus().equals("4")) {
                fetch = this.examine(fetch.getId(), false, null, xin.getRamount(), "XindovndNotify");
            }
            log.info("京都入账处理结果:" + fetch);
            return fetch;
        } else {
            log.error("xindovndNotify recharge fail");
            Result.breakWith("fail");
            return null;
        }
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public String xindovndToPay(String appid, String currencyId, int amount, String requestURL) throws IOException {
        log.info("xindovndToPay appid:" + appid + ",currencyId:" + currencyId + ",amount:" + amount);
        SysParam sysParam = paramService.fetch();
        UserRecharge userRecharge = add(amount, appid, PayChannel.XINDO_VND, currencyId, null);
        String notifyUrl = PayNotifyConstant.XIN_RECHARGE_NOTIFY_COM;
        if (requestURL.contains(PayNotifyConstant.REQUEST_URL_NET)) {
            notifyUrl = PayNotifyConstant.XIN_RECHARGE_NOTIFY_NET;
        }
        String respBody = XindovndUtil.send(sysParam.getXindovndToPayUrl(),
                XindovndUtil.createPayParam(String.valueOf(amount), sysParam.getXindovndMerchant(),
                        sysParam.getXindovndPaytype(), notifyUrl,
                        userRecharge.getNo(), sysParam.getXindovndSecret()));
        try {
            JSONObject jsonObject = JSONUtil.parseToJSON(respBody).getJSONObject("results");
            String url = jsonObject.getString("redirect");
            this.update(Chain.make("outNo", jsonObject.getString("tradeno"))
                            .add("address", url)
                            .add("paymentAt", Times.getTS())
                            .add("status", RechargeStatusEnum.PAID),
                    Cnd.where("id", "=", userRecharge.getId()));
            return url;
        } catch (Exception e) {
            if (sysParam.isDebug()) {
                Result.breakWith(JSONUtil.parseToJSON(respBody).getString("results"));
            }
            Result.breakWith(BizExceptionEnum.RETRY);
        }
        return null;
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public UserRecharge elephantNotify(ElephantRechargeDTO elephant) throws ParseException, UnsupportedEncodingException {
        log.info("elephantNotify recharge " + elephant);
        SysParam sysParam = paramService.fetch();
        if (ElephantUtil.verifyRechargeNotify(elephant, sysParam.getElephantSecret())) {
            UserRecharge fetch = this.fetch(Cnd.where("no", "=", elephant.getOuttradeno()));
            if (fetch.getAmount().compareTo(new BigDecimal(elephant.getAmount())) == 0 && elephant.getStatus().equals("1")) {
                fetch = this.examine(fetch.getId(), true, null, elephant.getRamount(), "ElephantNotify");
                this.update(Chain.make("paymentAt",
                        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(elephant.getEndtime()).getTime() / 1000L),
                        Cnd.where("id", "=", fetch.getId()));
            } else if (elephant.getStatus().equals("4")) {
                fetch = this.examine(fetch.getId(), false, null, elephant.getRamount(), "elephantNotify");
            }
            log.info("大象入账处理结果:" + fetch);
            return fetch;
        } else {
            log.error("elephantNotify recharge fail");
            Result.breakWith("fail");
            return null;
        }
    }

    @Override
    public boolean isRecharge(String userId) {
        // 这个要放缓存会好点
        String isRecharge = redisService.get("userId:recharge:" + userId);
        if (Strings.isBlank(isRecharge)) {
            StringBuffer sb = new StringBuffer();
            sb.append("select  id from user_recharge where status='SUCCESS' and channel !='PLAT_FORM' and userId='");
            sb.append(userId).append("' limit 1");
            List<UserRecharge> list = commonDBService.list(UserRecharge.class, sb.toString());
            if (list.size() > 0) {
                redisService.set("userId:recharged:" + userId, "yes");
                return true;
            }
            redisService.set("userId:recharged:" + userId, "no");
            return false;
        } else {
            if ("yes".equals(isRecharge)) {
                return true;
            } else {
                return false;
            }
        }

    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public String globalPayToPay(String appid, String currencyId, PayChannel channel, int amount, String requestURL) throws IOException {
        log.info("globalPayToPay appid:" + appid + ",currencyId:" + currencyId + ",amount:" + amount);
        SysParam sysParam = paramService.fetch();
        SysCurrency currency = sysCurrencyService.fetch(currencyId);
        UserRecharge userRecharge = add(amount, appid, channel, currencyId, null);
        // TMD GLOBALPAY
//        String notifyUrl = PayNotifyConstant.GLO_RECHARGE_NOTIFY_COM;
//        if (requestURL.contains(PayNotifyConstant.REQUEST_URL_NET)) {
//            notifyUrl = PayNotifyConstant.GLO_RECHARGE_NOTIFY_NET;
//        }

        String notifyUrl = PayNotifyConstant.GLO_RECHARGE_NOTIFY_NET;
        String respBody = GlobalPayUtil.send(sysParam.getGlobalPayToPayUrl(),
                GlobalPayUtil.createPayParam(String.valueOf(amount),
                        notifyUrl,
                        userRecharge.getNo(),
                        currency.getName(),
                        channel
                )
        );
        log.info("globalPayToPay respBody:" + respBody);
        JSONObject jsonObject = JSONUtil.parseToJSON(respBody);
        if ("SUCCESS".equals(jsonObject.getString("status"))) {
            String url = jsonObject.getString("order_data");
            this.update(Chain.make("outNo", jsonObject.getString("order_no"))
                            .add("address", url)
                            .add("paymentAt", Times.getTS())
                            .add("status", RechargeStatusEnum.PAID),
                    Cnd.where("id", "=", userRecharge.getId()));
            return url;
        } else if ("FAIL".equals(jsonObject.getString("status"))) {
            if (sysParam.isDebug()) {
                Result.breakWith(JSONUtil.parseToJSON(respBody).getString("results"));
            }
            Result.breakWith(jsonObject.getString("err_code") + ":" + jsonObject.getString("err_msg"));
            return null;
        } else {
            if (sysParam.isDebug()) {
                Result.breakWith(JSONUtil.parseToJSON(respBody).getString("results"));
            }
            Result.breakWith(BizExceptionEnum.RETRY);
            return null;
        }
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public UserRecharge globalPayNotify(GlobalPayRechargeDTO globalPay) throws ParseException, UnsupportedEncodingException {
        log.info("globalPayNotify recharge " + globalPay);
        String secret = GlobalPayParamConstant.IDR_SECRET;
        if (GlobalPayParamConstant.THB_MER_NO.equals(globalPay.getMer_no())) {
            secret = GlobalPayParamConstant.THB_SECRET;
        } else if (GlobalPayParamConstant.INR_MER_NO.equals(globalPay.getMer_no())) {
            secret = GlobalPayParamConstant.INR_SECRET;
        }
        if (GlobalPayUtil.verifyRechargeNotify(globalPay, secret)) {
            UserRecharge fetch = this.fetch(Cnd.where("no", "=", globalPay.getMer_order_no()));
            if (fetch.getAmount().compareTo(new BigDecimal(globalPay.getOrder_amount())) == 0 && "SUCCESS".equals(globalPay.getStatus())) {
                fetch = this.examine(fetch.getId(), true, null, globalPay.getPay_amount(), "GlobalPayNotify");
                this.update(Chain.make("paymentAt",
                        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(globalPay.getPay_time()).getTime() / 1000L),
                        Cnd.where("id", "=", fetch.getId()));
            } else {
                fetch = this.examine(fetch.getId(), false, null, globalPay.getPay_amount(), "GlobalPayNotify");
            }
            log.info("GlobalPay处理结果:" + fetch);
            return fetch;
        } else {
            log.error("GlobalPayNotify recharge fail，globalPayDTO:" + globalPay);
            Result.breakWith("fail");
            return null;
        }
    }

    @Override
    @Aop(TransAop.REPEATABLE_READ)
    public String elephantToPay(String appid, String currencyId, int amount, String requestURL) throws IOException {
        log.info("elephantToPay appid:" + appid + ",currencyId:" + currencyId + ",amount:" + amount);
        SysParam sysParam = paramService.fetch();
        UserRecharge userRecharge = add(amount, appid, PayChannel.ELEPHANT168, currencyId, null);
        String notifyUrl = PayNotifyConstant.ELE_RECHARGE_NOTIFY_COM;
        if (requestURL.contains(PayNotifyConstant.REQUEST_URL_NET)) {
            notifyUrl = PayNotifyConstant.ELE_RECHARGE_NOTIFY_NET;
        }
        String respBody = ElephantUtil.send(sysParam.getElephantToPayUrl(),
                ElephantUtil.createPayParam(String.valueOf(amount), sysParam.getElephantMerchant(),
                        sysParam.getElephantPaytype(), notifyUrl,
                        userRecharge.getNo(), sysParam.getElephantSecret()));
        try {
            JSONObject jsonObject = JSONUtil.parseToJSON(respBody).getJSONObject("results");
            String url = jsonObject.getString("redirect");
            this.update(Chain.make("outNo", jsonObject.getString("tradeno"))
                            .add("address", url)
                            .add("paymentAt", Times.getTS())
                            .add("status", RechargeStatusEnum.PAID),
                    Cnd.where("id", "=", userRecharge.getId()));
            return url;
        } catch (Exception e) {
            if (sysParam.isDebug()) {
                Result.breakWith(JSONUtil.parseToJSON(respBody).getString("results"));
            }
            Result.breakWith(BizExceptionEnum.RETRY);
        }
        return null;
    }

}
